package top.starshine.pack;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.google.auto.service.AutoService;
import com.sun.tools.javac.api.JavacTrees;
import com.sun.tools.javac.code.Flags;
import com.sun.tools.javac.processing.JavacProcessingEnvironment;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.TreeMaker;
import com.sun.tools.javac.tree.TreeScanner;
import com.sun.tools.javac.util.Context;
import com.sun.tools.javac.util.Name;
import com.sun.tools.javac.util.Names;
import sun.misc.Unsafe;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic;
import javax.tools.FileObject;
import javax.tools.StandardLocation;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.regex.Pattern;
import java.util.stream.Collectors;


@SupportedSourceVersion(SourceVersion.RELEASE_8)
@AutoService(SystemAnnotationAbstractProcessor.class)
@SupportedAnnotationTypes("top.starshine.pack.ToJson")
public class SystemAnnotationAbstractProcessor extends AbstractProcessor {
    private JavacTrees javacTrees;
    private ToJsonGenerator toJsonGenerator;
    /** 预编译正则表达式 */
    private static final Pattern FIELD_ACCESS_PATTERN =
            Pattern.compile(
                    ".*(private |private final |private transient|transient |public |public transient|public final |protected |protected final )$"
            );
    /** 收集根据配置扫描之后的 class 路径 */
    private final Set<String> indexedClasses = new HashSet<>();
    /** 系统扫描 rpc 接口 class 配置 */
    private SystemScanClassConfig systemScanClassConfig;
    /** 系统 rpc 扫描配置 */
    private static final String scanConfigFileName = "system-rcp-config.json";
    /** 系统 rpc 索引文件路径名称 */
    public static final String rpcInterfaceIndexFileName = "META-INF/system.rpc.components";

    // sunapi suppresses javac's warning about using Unsafe; 'all' suppresses eclipse's warning about the unspecified 'sunapi' key. Leave them both.
    // Yes, javac's definition of the word 'all' is quite contrary to what the dictionary says it means. 'all' does NOT include 'sunapi' according to javac.
    @SuppressWarnings({ "sunapi", "all" })
    private void disableJava9SillyWarning () {
        // JVM9 complains about using reflection to access packages from a module that aren't exported. This makes no sense; the whole point of reflection
        // is to get past such issues. The only comment from the jigsaw team lead on this was some unspecified mumbling about security which makes no sense,
        // as the SecurityManager is invoked to check such things. Therefore this warning is a bug, so we shall patch java to fix it.
        try {
            Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
            theUnsafe.setAccessible(true);
            Unsafe u = (Unsafe) theUnsafe.get(null);
            Class<?> cls = Class.forName("jdk.internal.module.IllegalAccessLogger");
            Field logger = cls.getDeclaredField("logger");
            u.putObjectVolatile(cls, u.staticFieldOffset(logger), null);
        } catch (Throwable t) {
            // We shall ignore it; the effect of this code failing is that the user gets to see a warning they remove with various --add-opens magic.
        }
    }

    @Override
    public synchronized void init (ProcessingEnvironment env) {
        super.init(env);
        disableJava9SillyWarning();
        ProcessingEnvironment realEnv = env;
        // 检查是否为代理对象
        if (Proxy.isProxyClass(processingEnv.getClass())) {
            try {
                // 处理 idea 热编译 ProcessingEnvironment 是代理对象
                InvocationHandler invocationHandler = Proxy.getInvocationHandler(processingEnv);
                for (Field field : invocationHandler.getClass().getDeclaredFields()) {
                    if (field.getType().getName().equals("java.lang.Object")) {
                        field.setAccessible(true);
                        Object fieldObject = field.get(invocationHandler);
                        if (fieldObject instanceof ProcessingEnvironment) {
                            realEnv = (ProcessingEnvironment) fieldObject;
                        }
                    }
                }
            } catch (Exception e) {
                // 记录错误或抛出异常
                env.getMessager().printMessage(Diagnostic.Kind.WARNING, "异常捕获 >>> " + e.getMessage());
            }
        }
        javacTrees = JavacTrees.instance(realEnv);// 语法树
        Context context = ((JavacProcessingEnvironment) realEnv).getContext();
        toJsonGenerator = new ToJsonGenerator(context);

        // 读取配 rpc scan 配置
        loadScanConfig();

    }

    @Override
    public boolean process (Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {

        // 扫描字段 >>> 收集
        final Set<String> classFieldNameSet = new HashSet<>();
        final List<JCTree.JCVariableDecl> classFieldList = new ArrayList<>();

        // 指定注解
        for (Element element : roundEnv.getElementsAnnotatedWith(ToJson.class)) {
            // 只有 class 才处理，enum 也是 class
            if (element.getKind().isClass()) {
                try {
                    // 获取被注解类的 JCClassDecl
                    JCTree tree = javacTrees.getTree(element);
                    if (!(tree instanceof JCTree.JCClassDecl)) {
                        processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "Element is not a class or enum: " + element);
                        continue;
                    }
                    // 执行扫描
                    tree.accept(new TreeScanner() {

                        @Override
                        public void visitVarDef (JCTree.JCVariableDecl varDecl) {
                            if (checkGenerateFieldAccess(varDecl.mods)) {
                                String name = varDecl.name.toString();
                                if (!classFieldNameSet.contains(name)) {
                                    classFieldList.add(varDecl);
                                    classFieldNameSet.add(name);
                                }
                            }
                            super.visitVarDef(varDecl);
                        }

                        private boolean checkGenerateFieldAccess (JCTree.JCModifiers fieldJCModifiers) {
                            String fieldAccessType = fieldJCModifiers.toString();
                            if (fieldJCModifiers.annotations.size() > 0) {
                                // 有注解的特殊处理，使用正则匹配末尾是不是符合规则
                                return FIELD_ACCESS_PATTERN.matcher(fieldAccessType).find();
                            } else {
                                // 只处理 private 和 private final 和 public、 public final
                                return "private ".equals(fieldAccessType)
                                        || "private final ".equals(fieldAccessType)
                                        || "private transient ".equals(fieldAccessType)
                                        || "transient ".equals(fieldAccessType)
                                        || "public ".equals(fieldAccessType)
                                        || "public transient".equals(fieldAccessType)
                                        || "public final ".equals(fieldAccessType)
                                        || "protected ".equals(fieldAccessType)
                                        || "protected final ".equals(fieldAccessType);
                            }
                        }

                    });
                    ToJson toJsonAnnotation = element.getAnnotation(ToJson.class);
                    // 生成 toJson 方法
                    toJsonGenerator.generateToJson((JCTree.JCClassDecl) javacTrees.getTree(element), classFieldList, toJsonAnnotation.callSuper());
                } catch (Exception e) {
                    processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "Failed to generate toJson: " + e.getMessage());
                }
                classFieldList.clear();
                classFieldNameSet.clear();
            }
        }

        // 必须有配置才能执行
        if (systemScanClassConfig != null) {
            // 处理周期结束时生成文件
            if (roundEnv.processingOver()) {
                // 开始生成索引文件
                generateIndexFile();
            } else {
                // 扫描当前 module 中的 java 后缀文件
                for (Element element : roundEnv.getRootElements()) {
                    if (element instanceof TypeElement) {
                        // 必须是接口或 class
                        if (element.getKind().isInterface() || element.getKind().isClass()) {
                            TypeElement typeElement = (TypeElement) element;
                            // 根据配置是否符合匹配
                            if (shouldIncludeClass(typeElement)) {
                                // 存储 >>> 获取类的全限定名
                                indexedClasses.add(typeElement.getQualifiedName().toString());
                            }
                        }
                    }
                }
            }
        }

        return false; // 允许其他处理器处理这些注解
    }

    private boolean shouldIncludeClass (TypeElement typeElement) {

        String className = typeElement.getQualifiedName().toString();
        String packageName = getPackageName(className);

        // 检查排除包模式
        if (systemScanClassConfig.matchesExcludePatterns(packageName)) {
            processingEnv.getMessager().printMessage(
                    Diagnostic.Kind.NOTE,
                    "Excluded class: " + className + " due to package exclusion pattern"
            );
            return false;
        }

        // 检查扫描包模式
        if (!systemScanClassConfig.matchesScanPatterns(packageName)) {
            processingEnv.getMessager().printMessage(
                    Diagnostic.Kind.NOTE,
                    "Excluded class: " + className + " - not matching scan patterns"
            );
            return false;
        }

        return true;
    }

    private String getPackageName (String className) {
        int lastDot = className.lastIndexOf('.');
        return lastDot > 0 ? className.substring(0, lastDot) : "";
    }

    private void loadScanConfig () {
        try {
            // 尝试从 CLASS_OUTPUT 读取（Maven 标准资源位置）
            FileObject configFile = processingEnv.getFiler().getResource(
                    StandardLocation.CLASS_OUTPUT,
                    "",
                    scanConfigFileName
            );
            parseConfigFile(configFile);
        } catch (Exception e1) {
            try {
                // 尝试从 SOURCE_PATH 读取
                FileObject configFile = processingEnv.getFiler().getResource(
                        StandardLocation.SOURCE_PATH,
                        "",
                        scanConfigFileName
                );
                parseConfigFile(configFile);
            } catch (Exception e2) {
                // 如果两个位置都找不到，使用空配置
                processingEnv.getMessager().printMessage(
                        Diagnostic.Kind.NOTE,
                        "systemRpcScan.json not found, using default configuration"
                );
            }
        }
    }

    private void parseConfigFile (FileObject configFile) {
        // D:\projects\top-starshine\top-starshine-cloud\starshine-client\messages-client\target\classes\system-rcp-config.json
        if ((new File(configFile.getName())).exists()) {
            try {
                InputStream inputStream = configFile.openInputStream();
                Throwable var3 = null;

                try {
                    BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
                    Throwable var5 = null;

                    try {

                        StringBuilder content = new StringBuilder();

                        String line;
                        while ((line = reader.readLine()) != null) {
                            content.append(line);
                        }

                        // 使用 Fastjson2 解析 JSON
                        JSONObject jsonObject = JSON.parseObject(content.toString());

                        // 是否启用配置
                        if (!jsonObject.getBooleanValue("enabled", false)) {
                            System.out.printf("[WARNING] %s 配置不启用，不执行索引构建%n", configFile.getName());
                            return;
                        }

                        this.systemScanClassConfig = new SystemScanClassConfig();

                        // 解析扫描包
                        if (jsonObject.containsKey("scanPackages")) {
                            JSONArray scanPackagesArray = jsonObject.getJSONArray("scanPackages");
                            Set<String> scanPackages = new HashSet<>();
                            for (int i = 0; i < scanPackagesArray.size(); i++) {
                                scanPackages.add(scanPackagesArray.getString(i));
                            }
                            systemScanClassConfig.setScanPackages(scanPackages);
                        }

                        // 解析排除包
                        if (jsonObject.containsKey("excludePackages")) {
                            JSONArray excludePackagesArray = jsonObject.getJSONArray("excludePackages");
                            Set<String> excludePackages = new HashSet<>();
                            for (int i = 0; i < excludePackagesArray.size(); i++) {
                                excludePackages.add(excludePackagesArray.getString(i));
                            }
                            systemScanClassConfig.setExcludePackages(excludePackages);
                        }

                        // 解析是否包含子包
                        if (jsonObject.containsKey("includeSubPackages")) {
                            systemScanClassConfig.setIncludeSubPackages(jsonObject.getBooleanValue("includeSubPackages"));
                        }

                        // 打印扫描配置信息
                        System.out.printf(
                                "[INFO] %s 找到配置 %s 开始执行索引构建,%n",
                                configFile.getName(), systemScanClassConfig.toString()
                        );
                        return;

                    } catch (Throwable var41) {
                        var5 = var41;
                        throw var41;
                    } finally {
                        if (var5 != null) {
                            try {
                                reader.close();
                            } catch (Throwable var40) {
                                var5.addSuppressed(var40);
                            }
                        } else {
                            reader.close();
                        }
                    }
                } catch (Throwable t) {
                    var3 = t;
                    throw t;
                } finally {
                    if (inputStream != null) {
                        if (var3 != null) {
                            try {
                                inputStream.close();
                            } catch (Throwable var39) {
                                var3.addSuppressed(var39);
                            }
                        } else {
                            inputStream.close();
                        }
                    }


                }
            } catch (Exception e) {
                System.out.println("[WARNING] to parse systemRpcScan.json: " + e.getMessage());
            }
        }
        System.out.printf("[INFO] %s 配置不存在，不执行索引构建%n", configFile.getName());
    }

    /** 构建索引文件 */
    private void generateIndexFile () {
        if (indexedClasses.isEmpty()) {
            return;
        }
        try {
            // 创建 META-INF/spring.components 文件
            FileObject file = processingEnv.getFiler().createResource(
                    StandardLocation.CLASS_OUTPUT,
                    "",
                    rpcInterfaceIndexFileName
            );
            // 写入文件
            try (PrintWriter writer = new PrintWriter(file.openWriter())) {
                boolean firstAfter = false;
                for (String entry : indexedClasses) {
                    if (firstAfter) {
                        writer.println();
                    } else {
                        firstAfter = true;
                    }
                    writer.print(entry);
                }
            }
            // 开始构建启动类型

        } catch (IOException e) {
            processingEnv.getMessager().printMessage(
                    Diagnostic.Kind.ERROR,
                    "Failed to create spring.components file: " + e.getMessage()
            );
        }
    }

    static class ToJsonGenerator {
        private final TreeMaker maker;
        private final Names names;
        private final JCTree.JCModifiers publicOverrideModifiers;
        private final Name genJsonBodyMethodName;
        private final JCTree.JCIdent genJsonBodyBeforeMethodName;
        private final JCTree.JCIdent returnStringBuilderType;
        private final JCTree.JCExpression jsonSerializableRef;

        // 常量字段的标识符（直接使用接口中的名称）
        private final JCTree.JCExpression COMMA_FIELD;
        private final JCTree.JCExpression QUOTE_FIELD;
        private final JCTree.JCExpression COLON_FIELD;

        public ToJsonGenerator (Context context) {
            this.maker = TreeMaker.instance(context);
            this.names = Names.instance(context);

            this.publicOverrideModifiers = maker.Modifiers(
                    Flags.PUBLIC, com.sun.tools.javac.util.List.of(
                            maker.Annotation(createFullClassPath("java.lang.Override"), com.sun.tools.javac.util.List.nil())));
            this.genJsonBodyMethodName = names.fromString("genJsonBody");
            this.genJsonBodyBeforeMethodName = maker.Ident(names.fromString("genJsonBodyBefore"));
            this.returnStringBuilderType = maker.Ident(names.fromString("StringBuilder"));


            // 创建 JsonSerializable 类的引用（假设全限定名为 com.example.JsonSerializable）
            this.jsonSerializableRef = maker.Select(createFullClassPath("top.starshine.pack"), names.fromString("JsonSerializableSupport"));

            // 生成常量字段表达式：JsonSerializable.COMMA, QUOTE, COLON
            COMMA_FIELD = maker.Select(this.jsonSerializableRef, names.fromString("COMMA"));
            QUOTE_FIELD = maker.Select(this.jsonSerializableRef, names.fromString("QUOTE"));
            COLON_FIELD = maker.Select(this.jsonSerializableRef, names.fromString("COLON"));

        }

        public void generateToJson (JCTree.JCClassDecl classDecl, java.util.List<JCTree.JCVariableDecl> fields, boolean callSuper) {
            final List<JCTree.JCStatement> statementList = new ArrayList<>();
            final Name sbName = names.fromString("sb");

            // 1. 初始化 StringBuilder
            JCTree.JCExpression superCall = maker.Apply(
                    com.sun.tools.javac.util.List.nil(),
                    // StringBuilder sb = super.genJsonBody();  或  StringBuilder sb = genJsonBodyBefore();
                    callSuper ? maker.Select(maker.Ident(names._super), genJsonBodyMethodName) : genJsonBodyBeforeMethodName,
                    com.sun.tools.javac.util.List.nil()
            );
            // 拼接 StringBuilder sb = ???
            statementList.add(
                    maker.VarDef(
                            maker.Modifiers(Flags.FINAL),
                            sbName,
                            returnStringBuilderType,
                            superCall
                    )
            );
            // 2. 处理字段追加逻辑
            for (int i = 0; i < fields.size(); i++) {
                JCTree.JCVariableDecl field = fields.get(i);
                // 2.1 判断是否需要拼接逗号
                boolean comma = callSuper || i != 0;
                // 2.2 生成键部分: "fieldName":
                addKeyPart(comma, statementList, sbName, field);
                // 2.3 调用 parse(sb, this.field)
                addParseCall(statementList, sbName, field);
            }

            // 3. 返回 StringBuilder 对象
            statementList.add(maker.Return(maker.Ident(sbName)));

            // 4. 构建方法体并添加到类
            classDecl.defs = classDecl.defs.append(
                    maker.MethodDef(
                            publicOverrideModifiers,
                            genJsonBodyMethodName,
                            returnStringBuilderType,
                            com.sun.tools.javac.util.List.nil(),
                            com.sun.tools.javac.util.List.nil(),
                            com.sun.tools.javac.util.List.nil(),
                            maker.Block(0, com.sun.tools.javac.util.List.from(statementList)),
                            null
                    )
            );
        }

        private void addKeyPart (boolean isComma, List<JCTree.JCStatement> statements, Name sbName, JCTree.JCVariableDecl field) {
            // 1. 构建字符序列（直接使用接口常量名）
            List<JCTree.JCExpression> parts = new ArrayList<>();

            // 处理逗号
            if (isComma) {
                parts.add(COMMA_FIELD); // 直接生成 COMMA
            }

            // 添加字段名前后的双引号
            parts.add(QUOTE_FIELD); // QUOTE

            // 动态生成字段名的每个字符
            String fieldName = field.getName().toString();
            for (char c : fieldName.toCharArray()) {
                parts.add(
                        maker.Literal(
                                com.sun.tools.javac.code.TypeTag.CHAR, // 显式标记为 CHAR 类型
                                (int) c  // JDK8 需要 int 值传递
                        )
                );
            }

            // 添加结束双引号和冒号
            parts.add(QUOTE_FIELD); // QUOTE
            parts.add(COLON_FIELD); // COLON

            // 2. 生成链式 append 调用
            JCTree.JCExpression currentExpr = maker.Ident(sbName); // 初始表达式：sb
            for (JCTree.JCExpression part : parts) {
                JCTree.JCExpression appendMethod = maker.Select(
                        currentExpr,
                        names.fromString("append")
                );
                currentExpr = maker.Apply(
                        com.sun.tools.javac.util.List.nil(),
                        appendMethod,
                        com.sun.tools.javac.util.List.of(part)
                );
            }

            // 插入 ensureCapacity 到语句列表（在 append 链之前）
            statements.add(maker.Exec(maker.Apply(
                    com.sun.tools.javac.util.List.nil(),
                    // 构建 sb.ensureCapacity(size) 语句
                    maker.Select(maker.Ident(sbName), names.fromString("ensureCapacity")),
                    // 计算 parts 的 size（编译时已知值） >>> 生成字面量表达式（如 maker.Literal(5)）
                    com.sun.tools.javac.util.List.of(maker.Literal(parts.size()))
            )));

            // 3. 将链式调用加入语句列表
            statements.add(maker.Exec(currentExpr));
        }

        private void addParseCall (List<JCTree.JCStatement> statements, Name sbName, JCTree.JCVariableDecl field) {
            // 生成JsonSerializableSupport.parse(sb, this.fieldName)
            JCTree.JCExpression parseCall = maker.Apply(
                    com.sun.tools.javac.util.List.nil(),
                    maker.Select(jsonSerializableRef, names.fromString("parse")),
                    com.sun.tools.javac.util.List.of(
                            maker.Ident(sbName),
                            maker.Select(
                                    maker.Ident(names.fromString("this")),
                                    field.getName()
                            )
                    )
            );
            statements.add(maker.Exec(parseCall));
        }

        // 递归生成包路径节点 (如 com.google.gson.Gson)
        private JCTree.JCExpression createFullClassPath (String packagePath) {
            String[] parts = packagePath.split("\\.");
            JCTree.JCExpression expr = maker.Ident(names.fromString(parts[0]));
            for (int i = 1; i < parts.length; i++) {
                expr = maker.Select(expr, names.fromString(parts[i]));
            }
            return expr;
        }

    }

}
